/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "iot_errno.h"
#include "iot_uart.h"
#include "los_sem.h"
#include "pinctrl.h"
#include "uart.h"
#if defined(CONFIG_UART_SUPPORT_DMA)
#include "dma.h"
#endif

#if defined(CONFIG_UART_SUPPORT_DMA)
#define UART_DMA_WIDTH                         0
#define UART_DMA_BURST_LENGTH                  2
#define UART_DMA_PRIORITY                      0
#define UART_DMA_TX_ENABLE                     1
#define UART_DMA_RX_ENABLE                     1
#endif

typedef struct{
    uint8_t hptr;
    uint8_t tptr;
    uint8_t revlen;
    uint8_t rxBlock;
}uart_rx_ptr;

#define RX_BUFFER_SIZE 16
#define RX_BAK_BUFFER_SIZE 256
static uint8_t g_iot_uart_rx_buffer[UART_BUS_MAX_NUM][RX_BUFFER_SIZE + 1] = {0};
static uint8_t g_iot_uart_rx_bak_buffer[UART_BUS_MAX_NUM][RX_BAK_BUFFER_SIZE + 1] = {0};
static uart_rx_ptr g_iot_uart_ptr[UART_BUS_MAX_NUM] = {0};
static uart_attr_t uart_line_config[UART_BUS_MAX_NUM] = {0};
static int uart_is_start[UART_BUS_MAX_NUM] = {0};

static void iot_uart0_rx_callback(const void *buffer, uint16_t length, bool error)
{
    errcode_t ret;
    const uint8_t *data = (const uint8_t *)buffer;
    if (error) {
        printf("*******uart0 error = %d*******\r\n", error);
    }
#ifndef CHIP_EDA
    if (length == 0) {
        return;
    }
#endif
    if(g_iot_uart_ptr[0].tptr + length < RX_BAK_BUFFER_SIZE)
    {
        memcpy_s(&g_iot_uart_rx_bak_buffer[0][g_iot_uart_ptr[0].tptr],
                 RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[0].tptr, data, length);
        g_iot_uart_ptr[0].tptr += length;
        if(g_iot_uart_ptr[0].hptr >= g_iot_uart_ptr[0].tptr - length && g_iot_uart_ptr[0].hptr <= g_iot_uart_ptr[0].tptr
         && g_iot_uart_ptr[0].revlen != 0)
        {
            g_iot_uart_ptr[0].hptr = g_iot_uart_ptr[0].tptr;
        }
    }
    else
    {
        memcpy_s(&g_iot_uart_rx_bak_buffer[0][g_iot_uart_ptr[0].tptr], RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[0].tptr,
                 data, RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[0].tptr);
        memcpy_s(&g_iot_uart_rx_bak_buffer[0][0], RX_BAK_BUFFER_SIZE, &data[RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[0].tptr],
                 length - (RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[0].tptr));
        g_iot_uart_ptr[0].tptr = g_iot_uart_ptr[0].tptr + length - RX_BAK_BUFFER_SIZE;
        if(((g_iot_uart_ptr[0].hptr > g_iot_uart_ptr[0].tptr + RX_BAK_BUFFER_SIZE - length) & (g_iot_uart_ptr[0].hptr > g_iot_uart_ptr[0].tptr))
          || ((g_iot_uart_ptr[0].hptr < g_iot_uart_ptr[0].tptr + RX_BAK_BUFFER_SIZE - length) & (g_iot_uart_ptr[0].hptr < g_iot_uart_ptr[0].tptr))
          && g_iot_uart_ptr[0].revlen != 0)
        {
            g_iot_uart_ptr[0].hptr = g_iot_uart_ptr[0].tptr;
        }
    }
    g_iot_uart_ptr[0].revlen += length;
}

static void iot_uart1_rx_callback(const void *buffer, uint16_t length, bool error)
{
    errcode_t ret;
    const uint8_t *data = (const uint8_t *)buffer;
    if (error) {
        printf("*******uart1 error = %d*******\r\n", error);
    }
#ifndef CHIP_EDA
    if (length == 0) {
        return;
    }
#endif
    if(g_iot_uart_ptr[1].tptr + length < RX_BAK_BUFFER_SIZE)
    {
        memcpy_s(&g_iot_uart_rx_bak_buffer[1][g_iot_uart_ptr[1].tptr],
                 RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[1].tptr, data, length);
        g_iot_uart_ptr[1].tptr += length;
        if(g_iot_uart_ptr[1].hptr >= g_iot_uart_ptr[1].tptr - length && g_iot_uart_ptr[1].hptr <= g_iot_uart_ptr[1].tptr
         && g_iot_uart_ptr[1].revlen != 0)
        {
            g_iot_uart_ptr[1].hptr = g_iot_uart_ptr[1].tptr;
        }
    }
    else
    {
        memcpy_s(&g_iot_uart_rx_bak_buffer[1][g_iot_uart_ptr[1].tptr], RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[1].tptr,
                 data, RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[1].tptr);
        memcpy_s(&g_iot_uart_rx_bak_buffer[1][0], RX_BAK_BUFFER_SIZE, &data[RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[1].tptr],
                 length - (RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[1].tptr));
        g_iot_uart_ptr[1].tptr = g_iot_uart_ptr[1].tptr + length - RX_BAK_BUFFER_SIZE;
        if(((g_iot_uart_ptr[1].hptr > g_iot_uart_ptr[1].tptr + RX_BAK_BUFFER_SIZE - length) & (g_iot_uart_ptr[1].hptr > g_iot_uart_ptr[1].tptr))
          || ((g_iot_uart_ptr[1].hptr < g_iot_uart_ptr[1].tptr + RX_BAK_BUFFER_SIZE - length) & (g_iot_uart_ptr[1].hptr < g_iot_uart_ptr[1].tptr))
          && g_iot_uart_ptr[1].revlen != 0)
        {
            g_iot_uart_ptr[1].hptr = g_iot_uart_ptr[1].tptr;
        }
    }
    g_iot_uart_ptr[1].revlen += length;
}

static void iot_uart2_rx_callback(const void *buffer, uint16_t length, bool error)
{
    errcode_t ret;
    const uint8_t *data = (const uint8_t *)buffer;
    if (error) {
        printf("*******uart2 error = %d*******\r\n", error);
    }
#ifndef CHIP_EDA
    if (length == 0) {
        return;
    }
#endif
    if(g_iot_uart_ptr[2].tptr + length < RX_BAK_BUFFER_SIZE)
    {
        memcpy_s(&g_iot_uart_rx_bak_buffer[2][g_iot_uart_ptr[2].tptr],
                 RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[2].tptr, data, length);
        g_iot_uart_ptr[2].tptr += length;
        if(g_iot_uart_ptr[2].hptr >= g_iot_uart_ptr[2].tptr - length && g_iot_uart_ptr[2].hptr <= g_iot_uart_ptr[2].tptr
         && g_iot_uart_ptr[2].revlen != 0)
        {
            g_iot_uart_ptr[2].hptr = g_iot_uart_ptr[2].tptr;
        }
    }
    else
    {
        memcpy_s(&g_iot_uart_rx_bak_buffer[2][g_iot_uart_ptr[2].tptr], RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[2].tptr,
                 data, RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[2].tptr);
        memcpy_s(&g_iot_uart_rx_bak_buffer[2][0], RX_BAK_BUFFER_SIZE, &data[RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[2].tptr],
                 length - (RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[2].tptr));
        g_iot_uart_ptr[2].tptr = g_iot_uart_ptr[2].tptr + length - RX_BAK_BUFFER_SIZE;
        if(((g_iot_uart_ptr[2].hptr >= g_iot_uart_ptr[2].tptr + RX_BAK_BUFFER_SIZE - length) & (g_iot_uart_ptr[2].hptr > g_iot_uart_ptr[2].tptr))
          || ((g_iot_uart_ptr[2].hptr < g_iot_uart_ptr[2].tptr + RX_BAK_BUFFER_SIZE - length) & (g_iot_uart_ptr[2].hptr < g_iot_uart_ptr[2].tptr))
          && g_iot_uart_ptr[2].revlen != 0)
        {
            g_iot_uart_ptr[2].hptr = g_iot_uart_ptr[2].tptr;
        }
    }
    g_iot_uart_ptr[2].revlen += length;
}

uart_rx_callback_t iot_uart_rx_callback[UART_BUS_MAX_NUM] = {iot_uart0_rx_callback, iot_uart1_rx_callback, iot_uart2_rx_callback};

unsigned int IoTUartInit(unsigned int id, const IotUartAttribute *param)
{
    if(param == NULL)
    {
        printf("[%s] error: param is NULL!\r\n", __FUNCTION__);
        return IOT_FAILURE;
    }
    if(id < UART_BUS_0 || id > UART_BUS_MAX_NUM)
    {
        printf("[%s] error: uart id%d is not support!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
    if(param->txBlock | param->pad)
    {
        printf("[%s] warning: txBlock and pad can only be 0!\r\n", __FUNCTION__);
    }
    uart_buffer_config_t uart_buffer_config;
    uart_pin_config_t uart_pin_config = {
        .tx_pin = 0,
        .rx_pin = 0,
        .cts_pin = PIN_NONE,
        .rts_pin = PIN_NONE,
    };
    if(param->baudRate <= 0 || param->dataBits < IOT_UART_DATA_BIT_5 || param->dataBits > IOT_UART_DATA_BIT_8 ||
        param->stopBits < IOT_UART_STOP_BIT_1 || param->stopBits > IOT_UART_STOP_BIT_2 ||
        param->parity < IOT_UART_PARITY_NONE
        || param->parity > IOT_UART_PARITY_EVEN)
    {
        printf("[%s] error: param is not support!\r\n", __FUNCTION__);
        return IOT_FAILURE;
    }
    uart_line_config[id].baud_rate = param->baudRate;
    if(param->baudRate >= 3000000)
    {
        printf("[%s] warning: the maximum baudRate is 3000000!\r\n", __FUNCTION__);
        uart_line_config[id].baud_rate = 3000000;
    }
    uart_line_config[id].data_bits = param->dataBits - IOT_UART_DATA_BIT_5;
    uart_line_config[id].stop_bits = param->stopBits - IOT_UART_STOP_BIT_1;
    uart_line_config[id].parity = param->parity;
    uapi_uart_deinit(id);
    uart_buffer_config.rx_buffer_size = RX_BUFFER_SIZE;
    uart_buffer_config.rx_buffer = &g_iot_uart_rx_buffer[id];
#if defined(CONFIG_UART_SUPPORT_DMA)
    uart_extra_attr_t extra_attr = {
        .tx_dma_enable = UART_DMA_TX_ENABLE,
        .tx_int_threshold = 0,
        .rx_dma_enable = UART_DMA_RX_ENABLE,
        .rx_int_threshold = 0
    };
    uapi_dma_init();
    uapi_dma_open();
    if (uapi_uart_init((uart_bus_t)id, &uart_pin_config, &uart_line_config[id], &extra_attr, &uart_buffer_config) != ERRCODE_SUCC) {
        return IOT_FAILURE;
    }
#else
    if (uapi_uart_init((uart_bus_t)id, &uart_pin_config, &uart_line_config[id], NULL, &uart_buffer_config) != ERRCODE_SUCC) {
        return IOT_FAILURE;
    }
#endif
    if(param->rxBlock == IOT_UART_BLOCK_STATE_BLOCK)
    {
        (void)uapi_uart_register_rx_callback(id, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
                                         RX_BUFFER_SIZE, iot_uart_rx_callback[id]);
        g_iot_uart_ptr[id].rxBlock = 1;
    }
    uart_is_start[id] = 1;
    return IOT_SUCCESS;
}

int IoTUartRead(unsigned int id, unsigned char *data, unsigned int dataLen)
{
    if(uart_is_start[id] != 1)
    {
        printf("[%s] error: uart id%d is not initialized!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
    if(data == NULL || dataLen <= 0 || dataLen > RX_BAK_BUFFER_SIZE)
    {
        printf("[%s] error: data or dataLen is illegal!\r\n", __FUNCTION__);
        return IOT_FAILURE;
    }
    if(id < UART_BUS_0 || id >= UART_BUS_MAX_NUM)
    {
        printf("[%s] error: uart id%d is not support!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
    if(g_iot_uart_ptr[id].rxBlock)
    {
        if(g_iot_uart_ptr[id].revlen < dataLen)
        {
            // printf("[%s] warning: no receive\r\n", __FUNCTION__);
            return IOT_FAILURE;
        }
        if(g_iot_uart_ptr[id].hptr + dataLen <= RX_BAK_BUFFER_SIZE)
        {
            if(memcpy_s(data, dataLen, &g_iot_uart_rx_bak_buffer[id][g_iot_uart_ptr[id].hptr], dataLen) != EOK) {
                printf("[%s] warning: memcpy_s fail\r\n", __FUNCTION__);
                return IOT_FAILURE;
            }
            g_iot_uart_ptr[id].hptr += dataLen;
        }
        else
        {
            if(memcpy_s(data, dataLen, &g_iot_uart_rx_bak_buffer[id][g_iot_uart_ptr[id].hptr], RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[id].hptr) != EOK) {
                printf("[%s] warning: memcpy_s fail\r\n", __FUNCTION__);
                return IOT_FAILURE;
            }
            if(memcpy_s(&data[RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[id].hptr], dataLen - (RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[id].hptr),
              &g_iot_uart_rx_bak_buffer[id][g_iot_uart_ptr[id].hptr], dataLen - (RX_BAK_BUFFER_SIZE - g_iot_uart_ptr[id].hptr)) != EOK) {
                printf("[%s] warning: memcpy_s fail\r\n", __FUNCTION__);
                return IOT_FAILURE;
            }
            g_iot_uart_ptr[id].hptr = g_iot_uart_ptr[id].hptr + dataLen - RX_BAK_BUFFER_SIZE;
        }
        g_iot_uart_ptr[id].revlen -= dataLen;
        if(g_iot_uart_ptr[id].hptr == g_iot_uart_ptr[id].tptr)
        {
            g_iot_uart_ptr[id].revlen = 0;
        }
    }
    else
    {
#if defined(CONFIG_UART_SUPPORT_DMA)
        uart_write_dma_config_t dma_cfg = {
            .src_width = UART_DMA_WIDTH,
            .dest_width = UART_DMA_WIDTH,
            .burst_length = UART_DMA_BURST_LENGTH,
            .priority = UART_DMA_PRIORITY
        };
        int ret = uapi_uart_read_by_dma((uart_bus_t)id, (uint8_t *)data, dataLen, &dma_cfg);
        if (ret != 0) {
            printf("[%s] warning: dataLen receive fail %x\r\n", __FUNCTION__, ret);
            return IOT_FAILURE;
        }
#else
        if(uapi_uart_read(id, (uint8_t *)data, dataLen, 0) != dataLen)
        {
            printf("[%s] warning: dataLen receive fail\r\n", __FUNCTION__);
            return IOT_FAILURE;
        }
#endif
    }
    return dataLen;
}

int IoTUartWrite(unsigned int id, const unsigned char *data, unsigned int dataLen)
{
    if(uart_is_start[id] != 1)
    {
        printf("[%s] error: uart id%d is not initialized!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
    if(data == NULL || dataLen <= 0)
    {
        printf("[%s] error: data or dataLen is illegal!\r\n", __FUNCTION__);
        return IOT_FAILURE;
    }
    if(id < UART_BUS_0 || id > UART_BUS_MAX_NUM)
    {
        printf("[%s] error: uart id%d is not support!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
#if defined(CONFIG_UART_SUPPORT_DMA)
    uart_write_dma_config_t dma_cfg = {
        .src_width = UART_DMA_WIDTH,
        .dest_width = UART_DMA_WIDTH,
        .burst_length = UART_DMA_BURST_LENGTH,
        .priority = UART_DMA_PRIORITY
    };
    if (uapi_uart_write_by_dma((uart_bus_t)id, (const uint8_t *)data, (uint32_t)dataLen,
        &dma_cfg) != 0) {
        printf("[%s] warning: dataLen send fail!\r\n", __FUNCTION__);
        return IOT_FAILURE;
    }
#else
    int ret = uapi_uart_write((uart_bus_t)id, (const uint8_t *)data, dataLen, 0);
    if(ret != (int32_t)dataLen)
    {
        printf("[%s] warning: dataLen send only %d!\r\n", __FUNCTION__, ret);
        return IOT_FAILURE;
    }
#endif
    return dataLen;
}

unsigned int IoTUartDeinit(unsigned int id)
{
    if(id < UART_BUS_0 || id > UART_BUS_MAX_NUM)
    {
        printf("[%s] error: uart id%d is not support!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
    if (uapi_uart_deinit((uart_bus_t)id) != ERRCODE_SUCC) {
        return IOT_FAILURE;
    }
    memset_s(&g_iot_uart_rx_buffer[id], RX_BUFFER_SIZE + 1, 0, RX_BUFFER_SIZE + 1);
    memset_s(&g_iot_uart_rx_bak_buffer[id], RX_BAK_BUFFER_SIZE + 1, 0, RX_BAK_BUFFER_SIZE + 1);
    memset_s(&g_iot_uart_ptr[id], sizeof(uart_rx_ptr), 0, sizeof(uart_rx_ptr));
    memset_s(&uart_line_config[id], sizeof(uart_attr_t), 0, sizeof(uart_attr_t));
    uart_is_start[id] = 0;
    return IOT_SUCCESS;
}

unsigned int IoTUartSetFlowCtrl(unsigned int id, IotFlowCtrl flowCtrl)
{
    if(id < UART_BUS_1 || id > UART_BUS_MAX_NUM)
    {
        printf("[%s] error: uart id%d is not support!\r\n", __FUNCTION__, id);
        return IOT_FAILURE;
    }
    uart_buffer_config_t uart_buffer_config;
    switch (flowCtrl)
    {
    case IOT_FLOW_CTRL_NONE:
        uart_line_config[id].flow_ctrl = UART_FLOW_CTRL_NONE;
        break;
    case IOT_FLOW_CTRL_RTS_CTS:
        uart_line_config[id].flow_ctrl = UART_FLOW_CTRL_RTS_CTS;
        break;
    default:
        printf("[%s] error: IotFlowCtrl %d is not support!\r\n", __FUNCTION__, flowCtrl);
        return IOT_FAILURE;
        break;
    }
    if(uart_is_start[id])
    {
        uart_pin_config_t uart_pin_config = {
            .tx_pin = 0,
            .rx_pin = 0,
            .cts_pin = PIN_NONE,
            .rts_pin = PIN_NONE,
        };
        uapi_uart_deinit(id);
        uart_buffer_config.rx_buffer_size = RX_BUFFER_SIZE;
        uart_buffer_config.rx_buffer = &g_iot_uart_rx_buffer[id];
        (void)uapi_uart_init(id, &uart_pin_config, &uart_line_config[id], NULL, &uart_buffer_config);
        if(g_iot_uart_ptr[id].rxBlock)
        {
            (void)uapi_uart_register_rx_callback(id, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
                                         RX_BUFFER_SIZE, iot_uart_rx_callback[id]);
        }
    }
    return IOT_SUCCESS;
}
